/*
 * Description:
 *  each line of input from stdin is filename of a wave file, output will be input_file_name.mp3
 *  when processing, a '*' will be printed to stdout for every 10% progress, 10 '*' means 
 *  the program has finished encoding the wave file and awaits next input.
 *	The program terminates when an empty line is received from stdin.
 */

#include "stdafx.h"
#include <windows.h>
#include "lame_enc_wrapper.h"
#include "SimpleIni.h"



// return true if the file is being opened for writing
BOOL isFileWriting(const char *strFile) {
	size_t numCharConverted;
	int strLen = strlen(strFile);
	wchar_t wstrFile[1000];
	mbstowcs_s(&numCharConverted, wstrFile, 1000, strFile, strLen );
	HANDLE hFileIn = CreateFile(wstrFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	// Check file open result
	if ( hFileIn == INVALID_HANDLE_VALUE && GetLastError() == ERROR_SHARING_VIOLATION ) {
		return true;
	}

	CloseHandle(hFileIn);
	return false;

}

int processFile(const char* strFileIn, const char *strFileOut) {

	FILE *pFileIn, *pFileOut;
	BE_CONFIG beConfig;
	DWORD		dwSamples		=0;
	DWORD		dwMP3Buffer		=0;
	HBE_STREAM	hbeStream		=0;
	BE_ERR		err				=0;

	PBYTE		pMP3Buffer		=NULL;
	PSHORT		pWAVBuffer		=NULL;

	

	memset(&beConfig,0,sizeof(beConfig));	// clear all fields



	CSimpleIni ini;
	ini.LoadFile( "wav2mp3.ini" );

	const wchar_t* sSampleRate = ini.GetValue( L"Lame-Config", L"dwSampleRate", L"44100" );
	
	beConfig.dwConfig = BE_CONFIG_LAME;
	beConfig.format.LHV1.dwStructVersion	= 1;
	beConfig.format.LHV1.dwStructSize		= sizeof(beConfig);		
	beConfig.format.LHV1.dwSampleRate		= _tstoi(sSampleRate);

//	beConfig.format.LHV1.dwReSampleRate		= 0;					// DON"T RESAMPLE
//	beConfig.format.LHV1.nMode				= BE_MP3_MODE_JSTEREO;	// OUTPUT IN STREO
//	beConfig.format.LHV1.dwBitrate			= 128;					// MINIMUM BIT RATE
//	beConfig.format.LHV1.nPreset			= LQP_STANDARD;		// QUALITY PRESET SETTING
//	beConfig.format.LHV1.dwMpegVersion		= MPEG1;				// MPEG VERSION (I or II)
//	beConfig.format.LHV1.dwPsyModel			= 0;					// USE DEFAULT PSYCHOACOUSTIC MODEL 
//	beConfig.format.LHV1.dwEmphasis			= 0;					// NO EMPHASIS TURNED ON
//	beConfig.format.LHV1.bOriginal			= TRUE;					// SET ORIGINAL FLAG

//	beConfig.format.LHV1.dwMaxBitrate		= 320;					// MAXIMUM BIT RATE
//	beConfig.format.LHV1.bCRC				= TRUE;					// INSERT CRC
//	beConfig.format.LHV1.bCopyright			= TRUE;					// SET COPYRIGHT FLAG	
//	beConfig.format.LHV1.bPrivate			= TRUE;					// SET PRIVATE FLAG
//	beConfig.format.LHV1.bWriteVBRHeader	= TRUE;					// YES, WRITE THE XING VBR HEADER
//	beConfig.format.LHV1.bEnableVBR			= TRUE;					// USE VBR
//	beConfig.format.LHV1.nVBRQuality		= 5;					// SET VBR QUALITY
//	beConfig.format.LHV1.bNoRes				= TRUE;					// No Bit resorvoir

	beInitStream(&beConfig, &dwSamples, &dwMP3Buffer, &hbeStream);

	// Try to open the WAV file, be sure to open it as a binary file!	
	pFileIn = fopen(strFileIn, "rb");

	// Check file open result
	if(pFileIn == NULL) {
		fprintf(stderr,"Error opening %s", strFileIn);
		return ERR_OPEN_INPUT_FILE;
	}


	BOOL bFileInChanging = isFileWriting(strFileIn);


	// Open MP3 file
	pFileOut = fopen(strFileOut,"wb");

	// Check file open result
	if(pFileOut == NULL) {
		fprintf(stderr,"Error creating file %s", strFileOut);
		return ERR_OPEN_OUTPUT_FILE;
	}

	// Init the MP3 Stream
	err = beInitStream(&beConfig, &dwSamples, &dwMP3Buffer, &hbeStream);

	// Check result
	if(err != BE_ERR_SUCCESSFUL) {
		fprintf(stderr,"Error opening encoding stream (%lu)", err);
		return ERR_INIT_STREAM;
	}


	// Allocate MP3 buffer
	pMP3Buffer = new BYTE[dwMP3Buffer];

	// Allocate WAV buffer
	pWAVBuffer = new SHORT[dwSamples];

	// Check if Buffer are allocated properly
	if(!pMP3Buffer || !pWAVBuffer) {
		fprintf(stderr, "Out of memory");
		return ERR_OUT_OF_MEMORY;
	}

	DWORD dwRead=0;
	DWORD dwWrite=0;
	DWORD dwDone=0;
	DWORD dwFileSize=0;
	short currPercent=0;
	short donePercent=0;

	// Seek to end of file
	fseek(pFileIn,0,SEEK_END);

	// Get the file size
	dwFileSize=ftell(pFileIn);

	// Seek back to start of WAV file,
	// but skip the first 44 bytes, since that's the WAV header
	fseek(pFileIn,0,SEEK_SET);

	const wchar_t* sConvertDelayEachTime = ini.GetValue( L"Lame-Config", L"dwConvertDelayEachTime", L"0" );
	DWORD dwConvertDelayEachTime = _tstoi(sConvertDelayEachTime);
	// Convert All PCM samples
	while ( true ) {
		
		dwRead = fread(pWAVBuffer,sizeof(SHORT),dwSamples,pFileIn);

		if ( dwRead == 0 ) {
			if ( !bFileInChanging ) {
				break;
			} else {
#if 0
				Sleep(3000);
#else
				if(dwConvertDelayEachTime)
					Sleep(dwConvertDelayEachTime);
#endif
				bFileInChanging = isFileWriting(strFileIn);
				continue;
			}
		}

		// Encode samples
		err = beEncodeChunk(hbeStream, dwRead, pWAVBuffer, pMP3Buffer, &dwWrite);

		// Check result
		if(err != BE_ERR_SUCCESSFUL) {
			beCloseStream(hbeStream);
			fprintf(stderr,"beEncodeChunk() failed (%lu)", err);
			return ERR_ENCODE_CHUNK;
		}
		
		// write dwWrite bytes that are returned in tehe pMP3Buffer to disk
		if(fwrite(pMP3Buffer,1,dwWrite,pFileOut) != dwWrite) {
			fprintf(stderr,"Output file write error");
			return ERR_WRITE_FILE;
		}

		dwDone += dwRead*sizeof(SHORT);

		/* remove progress calculation first, since I still dunno how to how intergrate this with
			external programs


		// only calculate and update progress when input is not changing
		if ( !bFileInChanging ) {
			donePercent = (short)(10*(float)dwDone/(float)(dwFileSize));

			for (int i=0; i<donePercent-currPercent; i++) {
				printf("*");
			}
			currPercent = donePercent;
		}
		*/

	}

	// Deinit the stream
	err = beDeinitStream(hbeStream, pMP3Buffer, &dwWrite);

	// Check result
	if(err != BE_ERR_SUCCESSFUL)
	{

		beCloseStream(hbeStream);
		fprintf(stderr,"beExitStream failed (%lu)", err);
		return ERR_EXIT_STREAM;
	}

	// Are there any bytes returned from the DeInit call?
	// If so, write them to disk
	if(dwWrite)
	{
		if( fwrite( pMP3Buffer, 1, dwWrite, pFileOut ) != dwWrite )
		{
			fprintf(stderr,"Output file write error");
			return ERR_WRITE_FILE;
		}
	}

	// close the MP3 Stream
	beCloseStream( hbeStream );

	// Delete WAV buffer
	delete [] pWAVBuffer;

	// Delete MP3 Buffer
	delete [] pMP3Buffer;

	// Close input file
	fclose( pFileIn );

	// Close output file
	fclose( pFileOut );

	// Write the VBR Tag
	beWriteVBRHeader( strFileOut );

	// Were done, return OK result
	return 0;
}


int main(int argc, char* argv[])
{


	char* strFileIn = argv[1];
	char* strFileOut = argv[2];

	if ( argc < 3 ) {
		printf("usage: wav2mp3 \"input.wav\" \"output.mp3\"\n");
		return 1;
	}

	int ret = initLame();
	if ( ret < 0 ) {
		return ret;
	}

	return processFile(strFileIn, strFileOut);
}

